package com.example.system.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.example.common.core.domain.TableDataInfo;
import com.example.common.core.enums.AnnouncementStatusEnum;
import com.example.common.core.enums.ResultCode;
import com.example.common.security.exception.ServiceException;
import com.example.system.domain.announcement.Announcement;
import com.example.system.domain.announcement.dto.AnnouncementQueryDTO;
import com.example.system.domain.announcement.dto.AnnouncementSaveDTO;
import com.example.system.domain.announcement.es.AnnouncementES;
import com.example.system.domain.announcement.vo.AnnouncementDetailVO;
import com.example.system.domain.announcement.vo.AnnouncementVO;
import com.example.system.elasticsearch.AnnouncementRepository;
import com.example.system.mapper.announcement.AnnouncementMapper;
import com.example.system.service.IAnnouncementService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;

@Service
@Transactional(readOnly = true)
public class AnnouncementServiceImpl implements IAnnouncementService {
    @Autowired
    private AnnouncementMapper announcementMapper;
    @Autowired
    private AnnouncementRepository announcementRepository;

    @Override
    public TableDataInfo list(AnnouncementQueryDTO announcementQueryDTO) {
        // 构造查询参数
        Sort sortBy = Sort.by(Sort.Direction.DESC, "createTime");
        Pageable pageable = PageRequest.of(announcementQueryDTO.getPageNum() - 1, announcementQueryDTO.getPageSize(), sortBy);
        Integer status = announcementQueryDTO.getStatus();
        String keyword = announcementQueryDTO.getTitle();
        
        Page<AnnouncementES> announcementESPage = null;
        try {
            if (status == null && StrUtil.isEmpty(keyword)) {
                // 查询全部数据
                announcementESPage = announcementRepository.findAll(pageable);
            } else if (StrUtil.isEmpty(keyword)) {
                // 根据状态筛选
                announcementESPage = announcementRepository.findAnnouncementByStatus(status, pageable);
            } else if (status == null) {
                // 根据关键词筛选
                announcementESPage = announcementRepository.findByTitleOrContent(keyword, keyword, pageable);
            } else {
                // 即根据关键词筛选，也根据状态进行筛选
                announcementESPage = announcementRepository.findByTitleOrContentAndStatus(keyword, keyword, status, pageable);
            }
        } catch (Exception e) {
            e.printStackTrace();
            // ES查询异常，从数据库查询并同步
            refreshQuestion();
        }
        
        // 如果ES中没有数据，从数据库同步
        if (announcementESPage == null || announcementESPage.getTotalElements() == 0) {
            refreshQuestion();
        }

        List<AnnouncementES> QuestionESList = announcementESPage != null ? announcementESPage.getContent() : new ArrayList<>();
        List<AnnouncementVO> announcementVOList = BeanUtil.copyToList(QuestionESList, AnnouncementVO.class);
        return TableDataInfo.success(announcementVOList, announcementESPage != null ? announcementESPage.getTotalElements() : 0L);
    }

    private void refreshQuestion() {
        List<Announcement> questionList = announcementMapper.selectList(new LambdaQueryWrapper<Announcement>());
        if (CollectionUtil.isEmpty(questionList)) {
            return;
        }
        List<AnnouncementES> questionESList = BeanUtil.copyToList(questionList, AnnouncementES.class);
        // 将题目列表同步至es缓存中
        // 实体已经存在就更新，不存在就新增
        announcementRepository.saveAll(questionESList);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int add(AnnouncementSaveDTO announcementAddDTO) {
        List<Announcement> announcementList = announcementMapper.selectList(new LambdaQueryWrapper<Announcement>()
                .eq(Announcement::getTitle, announcementAddDTO.getTitle()));
        if(CollectionUtil.isNotEmpty(announcementList)){
            throw new ServiceException(ResultCode.FAILED_ALREADY_EXISTS);
        }
        Announcement announcement = new Announcement();
        BeanUtil.copyProperties(announcementAddDTO, announcement);
        
        // 如果未设置状态，默认为待发布
        if (announcement.getStatus() == null) {
            announcement.setStatus(AnnouncementStatusEnum.DRAFT.getCode());
        }
        
        int result = announcementMapper.insert(announcement);
        
        // 同步添加到ES缓存
        try {
            AnnouncementES announcementES = new AnnouncementES();
            BeanUtil.copyProperties(announcement, announcementES);
            announcementRepository.save(announcementES);
        } catch (Exception e) {
            e.printStackTrace();
        }
        
        return result;
    }

    @Override
    public AnnouncementDetailVO detail(Long announcementId) {
        // 先从ES中查询
        try {
            AnnouncementES announcementES = announcementRepository.findById(announcementId).orElse(null);
            if (announcementES != null) {
                AnnouncementDetailVO announcementDetailVO = new AnnouncementDetailVO();
                BeanUtil.copyProperties(announcementES, announcementDetailVO);
                return announcementDetailVO;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        
        // ES中查询未果，从数据库查询
        Announcement announcement = announcementMapper.selectById(announcementId);
        if(announcement == null){
            throw new ServiceException(ResultCode.FAILED_NOT_EXISTS);
        }
        
        // 将数据同步到ES中
        try {
            AnnouncementES announcementES = new AnnouncementES();
            BeanUtil.copyProperties(announcement, announcementES);
            announcementRepository.save(announcementES);
        } catch (Exception e) {
            e.printStackTrace();
        }
        
        AnnouncementDetailVO announcementDetailVO = new AnnouncementDetailVO();
        BeanUtil.copyProperties(announcement, announcementDetailVO);
        return announcementDetailVO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int edit(AnnouncementSaveDTO announcementEditDTO) {
        Announcement announcement = announcementMapper.selectById(announcementEditDTO.getAnnouncementId());
        if(announcement==null){
            throw new ServiceException(ResultCode.FAILED_NOT_EXISTS);
        }
        announcement.setTitle(announcementEditDTO.getTitle());
        announcement.setContent(announcementEditDTO.getContent());
        
        // 更新状态
        if (announcementEditDTO.getStatus() != null) {
            announcement.setStatus(announcementEditDTO.getStatus());
        }
        
        int result = announcementMapper.updateById(announcement);
        
        // 同步更新ES缓存
        try {
            AnnouncementES announcementES = new AnnouncementES();
            BeanUtil.copyProperties(announcement, announcementES);
            announcementRepository.save(announcementES);
        } catch (Exception e) {
            e.printStackTrace();
        }
        
        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int delete(Long announcementId) {
        Announcement announcement = announcementMapper.selectById(announcementId);
        if(announcement==null){
            throw new ServiceException(ResultCode.FAILED_NOT_EXISTS);
        }
        int result = announcementMapper.deleteById(announcementId);
        
        // 从ES中删除
        try {
            announcementRepository.deleteById(announcementId);
        } catch (Exception e) {
            e.printStackTrace();
        }
        
        return result;
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int publish(Long announcementId) {
        Announcement announcement = announcementMapper.selectById(announcementId);
        if (announcement == null) {
            throw new ServiceException(ResultCode.FAILED_NOT_EXISTS);
        }
        
        // 待发布状态和已下架状态的公告都可以发布
        if (!AnnouncementStatusEnum.DRAFT.getCode().equals(announcement.getStatus()) 
            && !AnnouncementStatusEnum.UNPUBLISHED.getCode().equals(announcement.getStatus())) {
            throw new ServiceException(ResultCode.FAILED);
        }
        
        announcement.setStatus(AnnouncementStatusEnum.PUBLISHED.getCode());
        int result = announcementMapper.updateById(announcement);
        
        // 同步更新ES缓存
        try {
            AnnouncementES announcementES = new AnnouncementES();
            BeanUtil.copyProperties(announcement, announcementES);
            announcementRepository.save(announcementES);
        } catch (Exception e) {
            e.printStackTrace();
        }
        
        return result;
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int unpublish(Long announcementId) {
        Announcement announcement = announcementMapper.selectById(announcementId);
        if (announcement == null) {
            throw new ServiceException(ResultCode.FAILED_NOT_EXISTS);
        }
        
        // 只有已发布状态的公告才能下架
        if (!AnnouncementStatusEnum.PUBLISHED.getCode().equals(announcement.getStatus())) {
            throw new ServiceException(ResultCode.FAILED);
        }
        
        announcement.setStatus(AnnouncementStatusEnum.UNPUBLISHED.getCode());
        int result = announcementMapper.updateById(announcement);
        
        // 同步更新ES缓存
        try {
            AnnouncementES announcementES = new AnnouncementES();
            BeanUtil.copyProperties(announcement, announcementES);
            announcementRepository.save(announcementES);
        } catch (Exception e) {
            e.printStackTrace();
        }
        
        return result;
    }
}
